namespace Sirius.Collections
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;

    public class Tree<T> : ITree<T>
    {
        private readonly List<ITree<T>> children = new List<ITree<T>>();
        private readonly ITree<T> parent;

        public Tree()
            : this(null, default(T))
        {
        }

        public Tree(T element)
            :this(null, element)
        {
        }

        public Tree(ITree<T> parent, T element)
        {
            Element = element;
            this.parent = parent;
            Plain = new List<T>();
        }

        public ITree<T> Parent
        {
            get
            {
                return parent ?? this;
            }
        }

        public IEnumerator<T> GetEnumerator()
        {
            return Plain.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public IEnumerable<T> Plain
        {
            get; private set;
        }

        public T Element
        {
            get;
            private set;
        }

        public IEnumerable<ITree<T>> Children
        {
            get { return children; }
        }

        public ITree<T> Find<TKey>(TKey parentKey, Func<T, TKey> keySelector)
        {
            if(Equals(Element, default(T)))
            {
                if(Equals(parentKey, default(TKey)))
                    return this;
            }

            if (!Equals(Element, default(T)))
            {
                var key = keySelector(Element);

                if (Equals(key, parentKey))
                    return this;
            }

            foreach (var child in Children)
            {
                var parentTree = ( (Tree<T>) child ).Find(parentKey, keySelector);
                if (parentTree != null)
                    return parentTree;
            }

            return null;
        }

        public void Add(T item)
        {
            var node = new Tree<T>(item);
            ((IList<ITree<T>>)Children).Add(node);

            ((IList<T>)Plain).Add(item);
        }

        public void Add(ITree<T> item)
        {
            ((IList<ITree<T>>)Children).Add(item);
            ((IList<T>)Plain).Add(item.Element);
        }

        public bool Equals(ITree<T> other)
        {
            if (!Equals(Element, other.Element))
                return false;

            if(children.Count() != other.Children.Count())
                return false;

            for (var i = 0; i < children.Count(); i++)
            {
                var thisTree = children.ElementAt(i);
                var otherTree = other.Children.ElementAt(i);

                if (!Equals(thisTree, otherTree))
                    return false;
            }

            return true;
        }

        public override bool Equals(object obj)
        {
            if(obj is ITree<T>)
            {
                return Equals(obj as ITree<T>);
            }

            return false;
        }
    }
}